module Parser (command)
where

import Data 

import Text.ParserCombinators.Parsec.Token 
import Text.ParserCombinators.Parsec 
import Text.ParserCombinators.Parsec.Language

numero = natural haskell

parseOffset = let
	lastline 	= char '$' >> return LastLine
	absolute 	= numero >>= return . Absolute . fromInteger
	current 	= char '.' >> return Current
	previous1 	= char '-' >> numero >>= return . Prev. fromInteger 
	previous2 	= many1 (char '-') >>= return . Prev . length 
	next1 		= (space <|> char '+') >> numero >>= return . Next . fromInteger
	next2 		= many1 (char '+') >>= return . Next . length
	are c 		= do 
		char c 
		s <- many1 (noneOf "/")
		char c
		return s
	renext 		= are '/' >>= return . ReNext
	lastrenext 	= string "//" >> return LastReNext
	reprev 		= are '?' >>= return . RePrev
	lastreprev 	= string "??" >> return LastRePrev
	markedas 	= string "'" >> lower >>= return . MarkedAs
	in choice (map try [lastline,absolute,current,previous1,previous2,next1,next2,
		lastrenext,renext,lastreprev,reprev,markedas]) 

parseRange 	= let 
	couple 	= do 
		l <- parseOffset
		char ','
		r <- parseOffset
		return $ Range l r
	coma = char ',' >> return (Range (Absolute 1) LastLine)
	semicoma = char ';' >> return (Range Current LastLine)
	in choice (map try [coma,semicoma,couple])

defaultOR Append 		= ORO Current
defaultOR Insert 		= ORO Current
defaultOR Change 		= ORO Current
defaultOR Print 		= ORO Current
defaultOR (SmallG _)		= ORR (Range (Absolute 1) (Current))
defaultOR (BigG  _)		= ORR (Range (Absolute 1) (Current))
defaultOR Delete 		= ORO Current
defaultOR NoCommand 		= ORN
acceptOffsetOnly c (ORR _) 	= pzero <?> ("only offsets for function " ++ show c ++ ".")
acceptOffsetOnly _ _ 		= return ()

parseOffsetOrRange 
	= try (parseRange >>= return . ORR) 
	<|> (parseOffset >>= return .ORO) 
	<|> return ORN

rconst 	= return . const . return

parseCommand = let 
	append 	=  char 'a' >> eof >> return (\r -> acceptOffsetOnly Append  r >> return Append)
	insert	=  char 'i' >> eof >> return (\r -> acceptOffsetOnly Insert  r >> return Insert)
	change 	=  char 'c' >> eof >> rconst Change
	delete 	=  char 'd' >> eof >> rconst Delete
	print  	=  char 'p' >> eof >> rconst Print
	smallg 	=  char 'g' >> char '/' >> many1 (noneOf "/") 
		>>= \p -> char '/' >> eof >> rconst (SmallG p)
	bigg   	=  char 'G' >> char '/' >> many1 (noneOf "/") 
		>>= \p -> char '/' >> eof >> rconst (BigG p)
	nocomm 	=  eof >> return (\r -> acceptOffsetOnly NoCommand r >> return NoCommand)
	in 	choice (map try [append,insert,change,delete,print,smallg,bigg]) <|> nocomm

parser 	= do 
	r <- parseOffsetOrRange 
	c <- parseCommand >>= ($ r)
	return $ CC c $ case r of 
		ORN -> defaultOR c
		_   -> r

command 	:: String -> Either ParseError CompleteCommand 
command 	= parse parser "Command Parser"
	

